package parser;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Lexer {
	
	private ArrayList<String> lexRules;
	
	/**
	 * Use this in order to symbolize an expression.  Call the <i><b>lex</b><i> Method of this class in order to 
	 * retrieve the symbolized expression.  
	 */
	public Lexer(){
		lexRules();  //Initialize Rules...
	}
	
	private void lexRules(){
		lexRules = new ArrayList<String>();
		lexRules.add("\\+");
		lexRules.add("-");
		lexRules.add("\\*");
		lexRules.add("/");
		lexRules.add("\\(");
		lexRules.add("\\)");
		lexRules.add("\\n");
		lexRules.add("sin");
		lexRules.add("cos");
		lexRules.add("tan");
		lexRules.add("sqrt");
		lexRules.add("pow");
		lexRules.add("\\^");
		lexRules.add("~");
		lexRules.add("x");
		lexRules.add("[0-9]+(\\.[0-9]+)?");
	}
	
	/**
	 * Use this in order to symbolize an expression.  
	 * @param toLex - The expression to symbolize/tokenize into tokens.
	 * @return ListOfSymbols - List
	 * @throws LexError - If there is an unrecognized symbol
	 */
	public List<Token> lex(String toLex) throws LexError{
		String input = toLex.replaceAll(" ", "");  //Remove White spaces for tokenizing.
		
		List<Token> symbols = new ArrayList<Token>();
		for(String regEx : lexRules){
			Pattern pattern = Pattern.compile(regEx);
			Matcher matcher = pattern.matcher(input);
			while(matcher.find()){
				Token s = new Token();
				s.matchedString = matcher.group();
				s.begin = matcher.start();
				s.end = matcher.end();
				try {
					s.lexSymbol = LexSymbol.getInstance(s.matchedString);
					s.precedence = LexSymbol.getPrecedence(s.lexSymbol);
					s.isLeftAssociative = LexSymbol.isLeftAssociative(s.lexSymbol);
				} catch (LexError e) {
					throw e;
				}
				symbols.add(s);
			}
		}
		
		Collections.sort(symbols);
		
		//Remove the comma seperated Lists...  Not needed for parsing...
		for(int i = 0; i < symbols.size(); i++){
			if(symbols.get(i).lexSymbol == LexSymbol.COMMA){
				symbols.remove(i);
				i--;
			}
		}
		//May throw LexError
		checkLexingErrors(symbols, input);
		
		return symbols;
	}
	
	private void checkLexingErrors(List<Token> checkSymbols, String input) throws LexError{
		int lastEndPoint = 0;
		for(Token check : checkSymbols){
			if(check.begin - lastEndPoint > 1){
				String unrecognized = input.substring(lastEndPoint, check.begin);
				throw new LexError("Unrecognized symbol " + unrecognized);
			}
			lastEndPoint = check.end;
		}
	}
}
